package com.cmsz.xpay.common.unionpay.sign;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;


import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import com.cmsz.xpay.common.exception.DefaultReturnCode;
import com.cmsz.xpay.common.exception.UpayBusinessException;
import com.cmsz.xpay.common.util.KeyValueUtil;
/**
 * 银联签名验签
 * 支持JKS与PKCS12证书类型
 * @author Eshin
 *
 */
@Component("unionPaySignService")
public class UnionPaySignService implements ISignService{

	@Resource
	CertUtil certUtil;
	/**
	 * 请求报文签名(使用配置文件中配置的私钥证书加密)<br>
	 * 功能：对请求报文进行签名,并计算赋值certid,signature字段并返回<br>
	 * @param reqData 请求报文map<br>
	 * @param encoding 上送请求报文域encoding字段的值<br>
	 * @return　签名后的map对象<br>
	 */
	public Map<String, String> sign(Map<String, String> reqData,String encoding) {
		Map<String, String> submitData = filterBlank(reqData);
		//生成签名值并设置在map中
		signAndSet(submitData, encoding);
		return submitData;
	}
	/**
	 * 将签名后的map组装成key=value&key=value...
	 * @param reqData
	 * @param encoding
	 * @return
	 */
	public String  signStr(Map<String, String> reqData,String encoding){
		Map<String, String> submitData= sign(reqData,encoding);
		String signStr = KeyValueUtil.mapToString(submitData);
		return signStr;
	}
	
	/**
	 * 多证书签名(通过传入私钥证书路径和密码加密）<br>
	 * 功能：如果有多个商户号接入银联,每个商户号对应不同的证书可以使用此方法:传入私钥证书和密码(并且在acp_sdk.properties中 配置 acpsdk.singleMode=false)<br>
	 * @param reqData 请求报文map<br>
	 * @param certPath 签名私钥文件（带路径）<br>
	 * @param certPwd 签名私钥密码<br>
	 * @param encoding 上送请求报文域encoding字段的值<br>
	 * @return　签名后的map对象<br>
	 */
	public Map<String, String> sign(Map<String, String> reqData,String certPath, 
			String certPwd,String encoding,String certType) {
		Map<String, String> submitData = filterBlank(reqData);
		signByCertInfo(submitData,certPath,certPwd,encoding,certType);
		return submitData;
	}
	/**
	 * 多证书签名组装成key=value&key=value...
	 * @param reqData
	 * @param certPath
	 * @param certPwd
	 * @param encoding
	 * @return
	 */
	public String signStr(Map<String, String> reqData,String certPath, 
			String certPwd,String encoding,String certType) {
		Map<String, String> submitData= sign(reqData,certPath,certPwd,encoding,certType);
		String signStr = KeyValueUtil.mapToString(submitData);
		return signStr;
	}
	/**
	 * 验证签名(SHA-1摘要算法)<br>
	 * @param resData 返回报文数据<br>
	 * @param encoding 上送请求报文域encoding字段的值<br>
	 * @return true 通过 false 未通过<br>
	 */
	public boolean validateSign(Map<String, String> rspData, String encoding) {
		if (StringUtils.isBlank(encoding)) {
			encoding = UnionPayConstants.UTF_8_ENCODING;
		}
		String stringSign = rspData.get(UnionPayConstants.PARAM_SIGNATURE);
		String certId = rspData.get(UnionPayConstants.PARAM_CERTID);
		
		// 将Map信息转换成key1=value1&key2=value2的形式
		rspData.remove(UnionPayConstants.PARAM_SIGNATURE);
		String stringData = KeyValueUtil.mapToString(rspData);
		
		try {
			// 验证签名需要用银联发给商户的公钥证书.
			return validateBySha1WithRsa(certUtil.getValidateKey(certId), 
									     SecureUtil.base64Decode(stringSign.getBytes(encoding)), 
									     SecureUtil.sha1X16(stringData,encoding));
		} catch (Exception e) {
			throw new UpayBusinessException("验证签名失败:"+e.getMessage(), DefaultReturnCode.UPAY_014A06,e);
		}
	}
	

	/**
	 * 对控件支付成功返回的结果信息中data域进行验签（控件端获取的应答信息）
	 * @param jsonData json格式数据，例如：{"sign" : "J6rPLClQ64szrdXCOtV1ccOMzUmpiOKllp9cseBuRqJ71pBKPPkZ1FallzW18gyP7CvKh1RxfNNJ66AyXNMFJi1OSOsteAAFjF5GZp0Xsfm3LeHaN3j/N7p86k3B1GrSPvSnSw1LqnYuIBmebBkC1OD0Qi7qaYUJosyA1E8Ld8oGRZT5RR2gLGBoiAVraDiz9sci5zwQcLtmfpT5KFk/eTy4+W9SsC0M/2sVj43R9ePENlEvF8UpmZBqakyg5FO8+JMBz3kZ4fwnutI5pWPdYIWdVrloBpOa+N4pzhVRKD4eWJ0CoiD+joMS7+C0aPIEymYFLBNYQCjM0KV7N726LA==",  "data" : "pay_result=success&tn=201602141008032671528&cert_id=68759585097"}
	 * @return 是否成功
	 */
	public  boolean validateAppRspSign(String jsonData, String encoding) {
		if (StringUtils.isBlank(encoding)) {
			encoding = UnionPayConstants.UTF_8_ENCODING;
		}

        Pattern p = Pattern.compile("\\s*\"sign\"\\s*:\\s*\"([^\"]*)\"\\s*");
		Matcher m = p.matcher(jsonData);
		if(!m.find()) return false;
		String sign = m.group(1);

		p = Pattern.compile("\\s*\"data\"\\s*:\\s*\"([^\"]*)\"\\s*");
		m = p.matcher(jsonData);
		if(!m.find()) return false;
		String data = m.group(1);

		p = Pattern.compile("cert_id=(\\d*)");
		m = p.matcher(jsonData);
		if(!m.find()) return false;
		String certId = m.group(1);

		try {
			// 验证签名需要用银联发给商户的公钥证书.
			return validateBySha1WithRsa(certUtil.getValidateKey(certId),
		                                 SecureUtil.base64Decode(sign.getBytes(encoding)),
		                                 SecureUtil.sha1X16(data,encoding));
		} catch (Exception e) {
			throw new UpayBusinessException("验证签名失败:"+e.getMessage(), DefaultReturnCode.UPAY_014A06,e);
		}
	}
	

	/**
	 * 过滤请求报文中的空字符串或者空字符串
	 * @param contentData
	 * @return
	 */
	public  Map<String, String> filterBlank(Map<String, String> contentData){
		Map<String, String> submitFromData = new HashMap<String, String>();
		Set<String> keyset = contentData.keySet();
		
		for(String key:keyset){
			String value = contentData.get(key);
			if (StringUtils.isNotBlank(value)) {
				// 对value值进行去除前后空处理
				submitFromData.put(key, value.trim());
			}
		}
		return submitFromData;
	}
	

	/**
	 * 生成签名值(SHA1摘要算法)
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码格式
	 * @return 签名是否成功
	 */
	public boolean signAndSet(Map<String, String> data, String encoding) {
		//设置默认编码格式
		if (StringUtils.isBlank(encoding)) {
			encoding = UnionPayConstants.UTF_8_ENCODING;
		}
		// 设置签名证书序列号
		data.put(UnionPayConstants.PARAM_CERTID, certUtil.getSignCertId());
		// 去除signature,将Map信息转换成key1=value1&key2=value2的形式
		data.remove(UnionPayConstants.PARAM_SIGNATURE);
		String stringData = KeyValueUtil.mapToString(data);
		byte[] byteSign = null;
		String stringSign = null;
		try {
			// 通过SHA1进行摘要并转16进制
			byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);
            // 签名\base64编码
			byteSign = SecureUtil.base64Encode(
					              signBySha1WithRsa(certUtil.getSignCertPrivateKey(), signDigest));
			stringSign = new String(byteSign);
			// 设置签名域值
			data.put(UnionPayConstants.PARAM_SIGNATURE, stringSign);
			return true;
		} catch (Exception e) {
			throw new UpayBusinessException("生成签名失败:"+e.getMessage(),DefaultReturnCode.UPAY_014A06,e);
		}
	}

	/**
	 * 通过传入的证书绝对路径和证书密码读取签名证书进行签名并返回签名值<br>
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码
	 * @param certPath
	 *            证书绝对路径
	 * @param certPwd
	 *            证书密码
	 * @param certTpye
	 *            证书了类型
	 * @return 签名值
	 */
	public boolean signByCertInfo(Map<String, String> data,String certPath,
			String certPwd,String encoding,String certTpye) {
		if (StringUtils.isBlank(encoding)) {
			encoding = UnionPayConstants.UTF_8_ENCODING;
		}
		if (StringUtils.isBlank(certPath) || StringUtils.isBlank(certPwd)) {
			return false;
		}
		// 设置签名证书序列号
		data.put(UnionPayConstants.PARAM_CERTID,
				 certUtil.getCertIdByKeyStoreMap(certPath, certPwd,certTpye));
		// 将Map信息转换成key1=value1&key2=value2的形式
		data.remove(UnionPayConstants.PARAM_SIGNATURE);
		String stringData = KeyValueUtil.mapToString(data);

		byte[] byteSign = null;
		String stringSign = null;
		try {
			byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);
            //签名\base64编码
			byteSign = SecureUtil.base64Encode(
					       signBySha1WithRsa(
					               certUtil.getSignCertPrivateKeyByStoreMap(certPath, certPwd,	certTpye),

					                      signDigest));
			stringSign = new String(byteSign);
			// 设置签名域值
			data.put(UnionPayConstants.PARAM_SIGNATURE, stringSign);
			return true;
		} catch (Exception e) {
			throw new UpayBusinessException("生成签名失败:"+e.getMessage(),DefaultReturnCode.UPAY_014A06,e);
		}
	}
	/**
	 * Sha1WithRsa签名方式
	 * 
	 * @param privateKey
	 *            私钥
	 * @param data
	 *            待签名数据
	 * @return 结果 
	 * @throws Exception
	 */
	public byte[] signBySha1WithRsa(PrivateKey privateKey, byte[] data)throws Exception {
		return SecureUtil.signByPrivateKey(
				privateKey, data, UnionPayConstants.BC_PROV_ALGORITHM_SHA1RSA);
	}
	/**
	 * Sha1WithRsa验证签名
	 * 
	 * @param publicKey
	 *            公钥
	 * @param signData
	 *            签名数据
	 * @param srcData
	 *            摘要
	 * @return
	 * @throws Exception
	 */
	public boolean validateBySha1WithRsa(PublicKey publicKey,byte[] signData, 
			                          byte[] srcData) throws Exception {

		return SecureUtil.validateByPublicKey(
				publicKey,signData,srcData,UnionPayConstants.BC_PROV_ALGORITHM_SHA1RSA);
	}
	
}
